/* * @(#)JComponent.java 2.61 98/04/10 * * Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved. * * This software is the confidential and proprietary information of Sun * Microsystems, Inc. ("Confidential Information"). You shall not * disclose such Confidential Information and shall use it only in * accordance with the terms of the license agreement you entered into * with Sun. * * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING * THIS SOFTWARE OR ITS DERIVATIVES. * */ package com.sun.java.swing; import java.awt.Component; import java.awt.Container; import java.awt.Color; import java.awt.Cursor; import java.awt.Window; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Frame; import java.awt.Dimension; import java.awt.Insets; import java.awt.Graphics; import java.awt.Window; import java.awt.AWTEvent; import java.awt.Rectangle; import java.util.Hashtable; import java.util.Dictionary; import java.util.Enumeration; import java.util.Locale; import java.util.Vector; import java.awt.Point; import java.awt.Image; import java.awt.LayoutManager2; import java.awt.Color; import java.awt.event.*; import java.beans.*; import java.applet.Applet; import java.io.Serializable; import java.io.ObjectOutputStream; import java.io.ObjectInputStream; import java.io.IOException; import com.sun.java.swing.border.*; import com.sun.java.swing.event.*; import com.sun.java.swing.plaf.*; import com.sun.java.accessibility.*; /** * The base class for the Swing components. JComponent provides: * * For more information on these subjects, see the * Swing package description *

* Warning: serialized objects of this class will not be compatible with * future swing releases. The current serialization support is appropriate * for short term storage or RMI between Swing1.0 applications. It will * not be possible to load serialized Swing1.0 objects with future releases * of Swing. The JDK1.2 release of Swing will be the compatibility * baseline for the serialized form of Swing objects. * * @see KeyStroke * @see Action * @see #setBorder * @see #registerKeyboardAction * @see JOptionPane * @see #setDebugGraphicsOptions * @see #setToolTipText * @see #setAutoscrolls * * @version 2.61 04/10/98 * @author Hans Muller * @author Arnaud Weber */ public abstract class JComponent extends Container implements Serializable { /* The following fields support set methods for the corresponding * java.awt.Component properties. */ private Dimension preferredSize; private Dimension minimumSize; private Dimension maximumSize; private Float alignmentX; private Float alignmentY; private AncestorNotifier ancestorNotifier; Rectangle _bounds = new Rectangle(); /* Backing store for JComponent properties and listeners */ protected ComponentUI ui; protected EventListenerList listenerList = new EventListenerList(); private Hashtable clientProperties; private VetoableChangeSupport vetoableChangeSupport; private Autoscroller autoscroller; private int flags; private Border border; private transient Rectangle tmpRect; /** * Constant used for registerKeyboardAction() which * means that the command should be invoked when * the component has the focus. */ public static final int WHEN_FOCUSED = 0; /** * Constant used for registerKeyboardAction() which * means that the comand should be invoked when the receiving * component is an ancestor of the focused component or is * itself the focused component. */ public static final int WHEN_ANCESTOR_OF_FOCUSED_COMPONENT = 1; /** * Constant used for registerKeyboardAction() which * means that the command should be invoked when * the receiving component is in the window that has the focus * or is itself the focused component. */ public static final int WHEN_IN_FOCUSED_WINDOW = 2; /** * Constant used by some of the apis to mean that no condition is defined. */ public static final int UNDEFINED_CONDITION = -1; /** * The key used by JComponent to access keyboard bindings. */ private static final String KEYBOARD_BINDINGS_KEY = "_KeyboardBindings"; /** * The comment to display when the cursor is over the component, * also known as a "value tip", "flyover help", or "flyover label". */ public static final String TOOL_TIP_TEXT_KEY = "ToolTipText"; private static final String NEXT_FOCUS = "nextFocus"; /** Private flags **/ private static final int REQUEST_FOCUS_DISABLED = 0; private static final int IS_DOUBLE_BUFFERED = 1; private static final int ANCESTOR_USING_BUFFER = 2; private static final int IS_PAINTING_TILE = 3; private static final int HAS_FOCUS = 4; private static final int IS_OPAQUE = 5; /** * Default JComponent constructor. This constructor does * no initialization beyond calling the Container constructor, * e.g. the initial layout manager is null. */ public JComponent() { super(); enableEvents(AWTEvent.FOCUS_EVENT_MASK); } /** * Resets the UI property to a value from the current look and feel. * JComponent subclasses must override this method like this: *

     *   public void updateUI() {
     *      setUI((SliderUI)UIManager.getUI(this);
     *   }
     *  
* * @see #setUI * @see UIManager#getLookAndFeel * @see UIManager#getUI */ public void updateUI() {} /** * Set the look and feel delegate for this component. * JComponent subclasses generally override this method * to narrow the argument type, e.g. in JSlider: *
     * public void setUI(SliderUI newUI) {
     *     super.setUI(newUI);
     * }
     *  
*

* Additionaly JComponent subclasses must provide a getUI * method that returns the correct type, e.g. *

     * public SliderUI getUI() {
     *     return (SliderUI)ui;
     * }
     * 
* * @see #updateUI * @see UIManager#getLookAndFeel * @see UIManager#getUI */ protected void setUI(ComponentUI newUI) { /* We do not check that the UI instance is different * before allowing the switch in order to enable the * same UI instance *with different default settings* * to be installed. */ if (ui != null) { ui.uninstallUI(this); } ComponentUI oldUI = ui; ui = newUI; if (ui != null) { ui.installUI(this); } invalidate(); firePropertyChange("UI", oldUI, ui); } /** * Return the UIDefaults key used to look up the name of the * swing.plaf.ComponentUI class that defines the look and feel * for this component. Most applications will never need to * call this method. Subclasses of JComponent that support * pluggable look and feel should override this method to * return the name of the ComponentUI subclass that defines * their look and feel. * * @return The name of a ComponentUI subclass. * @see UIDefaults#getUI * @beaninfo * expert: true * description: UIClassID */ public String getUIClassID() { return "not a pluggable look and feel class"; } /** * Returns the graphics object used to paint this component. * If DebugGraphics is turned on we create a new DebugGraphics * object if neccessary otherwise we just configure the * specified graphics objects foreground and font. * * @return A Graphics object configured for this component */ protected Graphics getComponentGraphics(Graphics g) { Graphics componentGraphics = g; if (ui != null) { if ((DebugGraphics.debugComponentCount() != 0) && (shouldDebugGraphics() != 0) && !(g instanceof DebugGraphics)) { if(g instanceof SwingGraphics) { if(!(((SwingGraphics)g).subGraphics() instanceof DebugGraphics)) { componentGraphics = new DebugGraphics(((SwingGraphics)g).subGraphics(),this); componentGraphics = SwingGraphics.createSwingGraphics(componentGraphics); } } else { componentGraphics = new DebugGraphics(g,this); } } } componentGraphics.setColor(getForeground()); componentGraphics.setFont(getFont()); return componentGraphics; } /** * If the UI delegate is non-null, call its paint * method. We pass the delegate a copy of the Graphics * object to protect the rest of the paint code from * irrevocable changes (e.g. Graphics.translate()). * * @see #paint */ protected void paintComponent(Graphics g) { if (ui != null) { Graphics scratchGraphics = SwingGraphics.createSwingGraphics(g.create()); try { ui.update(scratchGraphics, this); } finally { scratchGraphics.dispose(); } } } /** * Paint this component's children. * If shouldUseBuffer is true, no component ancestor has a buffer and * the component children can use a buffer if they have one. * Otherwise, one ancestor has a buffer currently in use and children * should not use a buffer to paint. * @see #paint * @see java.awt.Container#paint */ protected void paintChildren(Graphics g) { boolean isJComponent; boolean shouldRecycle = false; SwingGraphics sg; if(g instanceof SwingGraphics) { sg = (SwingGraphics)g; } else { sg = SwingGraphics.createSwingGraphics(g); shouldRecycle = true; } synchronized(getTreeLock()) { for (int i = getComponentCount() - 1 ; i >= 0 ; i--) { Component comp = getComponent(i); if (comp != null && isLightweightComponent(comp) && (comp.isVisible() == true)) { Rectangle cr; isJComponent = (comp instanceof JComponent); if(isJComponent) { if(tmpRect == null) { tmpRect = new Rectangle(); } cr = tmpRect; ((JComponent)comp).getBounds(cr); } else { cr = comp.getBounds(); } if (sg.isClipIntersecting(cr)) { Graphics cg = SwingGraphics.createGraphics( g, cr.x, cr.y, cr.width, cr.height); boolean shouldSetFlagBack = false; try { if(isJComponent) { if(getFlag(ANCESTOR_USING_BUFFER)) { ((JComponent)comp).setFlag(ANCESTOR_USING_BUFFER,true); shouldSetFlagBack = true; } if(getFlag(IS_PAINTING_TILE)) { ((JComponent)comp).setFlag(IS_PAINTING_TILE,true); shouldSetFlagBack = true; } ((JComponent)comp).paint(cg); } else { comp.paint(cg); } } finally { cg.dispose(); if(shouldSetFlagBack) { ((JComponent)comp).setFlag(ANCESTOR_USING_BUFFER,false); ((JComponent)comp).setFlag(IS_PAINTING_TILE,false); } } } } } } if(shouldRecycle) { sg.recycle(); } } /** * Paint the component's border. * * @see #paint * @see #setBorder */ protected void paintBorder(Graphics g) { Border border = getBorder(); if (border != null) { border.paintBorder(this, g, 0, 0, getWidth(), getHeight()); } } /** * Calls paint(g). Doesn't clear the background but see * ComponentUI.update() which is called by paintComponent. * * @see #paint * @see #paintComponent * @see com.sun.java.swing.plaf.ComponentUI */ public void update(Graphics g) { paint(g); } /** * This method is invoked by Swing to draw components. * Applications should not invoke paint directly, * but should instead use the repaint method to * schedule the component for redrawing. *

* This method actually delegates the work of painting to three * protected methods: paintComponent, paintBorder, * and paintChildren. They're called in the order * listed to ensure that children appear on top of component itself. * Generally speaking, the component and its children should not * paint in the insets area allocated to the border. Subclasses can * just override this method, as always. A subclass that just * wants to specialize the UI (look and feel) delegates paint * method should just override paintComponent. * * @see #paintComponent * @see #paintBorder * @see #paintChildren * @see #getComponentGraphics * @see #repaint */ public void paint(Graphics g) { boolean shouldClearPaintFlags = false; if (getWidth() == 0 || getHeight() == 0) { return; } Graphics componentGraphics = getComponentGraphics(g); boolean shouldRecycle; if((componentGraphics instanceof SwingGraphics)) { shouldRecycle = false; } else { shouldRecycle = true; } SwingGraphics co = SwingGraphics.createSwingGraphics(componentGraphics); Image offscr = null; RepaintManager repaintManager = RepaintManager.currentManager(this); int clipX = co.getClipX(); int clipY = co.getClipY(); int clipW = co.getClipWidth(); int clipH = co.getClipHeight(); if(clipW > getWidth()) { clipW = getWidth(); } if(clipH > getHeight()) { clipH = getHeight(); } if(getParent() != null && !(getParent() instanceof JComponent)) { adjustPaintFlags(); shouldClearPaintFlags = true; } if(repaintManager.isDoubleBufferingEnabled() && !getFlag(ANCESTOR_USING_BUFFER) && isDoubleBuffered()) { int bw,bh; int x,y,maxx,maxy; offscr = repaintManager.getOffscreenBuffer(this,clipW,clipH); if(shouldRecycle) { co.recycle(); shouldRecycle = false; } co = SwingGraphics.createSwingGraphics(offscr.getGraphics()); co.translate(-clipX,-clipY); bw = offscr.getWidth(null); bh = offscr.getHeight(null); if(bw > clipW) { bw = clipW; } if(bh > clipH) { bh = clipH; } try { setFlag(ANCESTOR_USING_BUFFER,true); setFlag(IS_PAINTING_TILE,true); for(x = 0, maxx = clipW; x < maxx ; x += bw ) { for(y=0, maxy = clipH; y < maxy ; y += bh) { if((y+bh) >= maxy && (x+bw) >= maxx) setFlag(IS_PAINTING_TILE,false); co.translate(-x,-y); co.setClip(clipX+x,clipY + y,bw,bh); if(!rectangleIsObscured(clipX,clipY,bw,bh)) { paintComponent(co); paintBorder(co); } paintChildren(co); g.drawImage(offscr,clipX + x,clipY + y,this); co.translate(x,y); } } } finally { setFlag(ANCESTOR_USING_BUFFER,false); setFlag(IS_PAINTING_TILE,false); co.dispose(); } } else { if (!rectangleIsObscured(clipX,clipY,clipW,clipH)) { paintComponent(co); paintBorder(co); } paintChildren(co); } if(shouldRecycle) { co.recycle(); } if(shouldClearPaintFlags) { setFlag(ANCESTOR_USING_BUFFER,false); setFlag(IS_PAINTING_TILE,false); } } private void adjustPaintFlags() { JComponent jparent = null; Container parent; for(parent = getParent() ; parent != null ; parent = parent.getParent()) { if(parent instanceof JComponent) { jparent = (JComponent) parent; if(jparent.getFlag(ANCESTOR_USING_BUFFER)) setFlag(ANCESTOR_USING_BUFFER, true); if(jparent.getFlag(IS_PAINTING_TILE)) setFlag(IS_PAINTING_TILE, true); break; } } } /** * Returns true if the receiving component is currently painting a tile. * If this method returns true, paint will be called again for another * tile. This method returns false if you are not painting a tile or * if the last tile is painted. * Use this method to keep some state you might need between tiles. */ public boolean isPaintingTile() { return getFlag(IS_PAINTING_TILE); } /** * Override this method and return true if your component is the root of * of a component tree with its own focus cycle. */ public boolean isFocusCycleRoot() { return false; } /** * Override this method and return true if your JComponent manages focus. * If your component manages focus, the focus manager will handle your * component's children. All key event will be sent to your key listener * including TAB and SHIFT+TAB. CONTROL + TAB and CONTROL + SHIFT + TAB * will move the focus to the next / previous component. */ public boolean isManagingFocus() { return false; } /** * Specifies the next component to get the focus after this one, * for example, when the tab key is pressed. Invoke this method * to override the default focus-change sequence. * @beaninfo * expert: true * description: The next component to get focus after this one. */ public void setNextFocusableComponent(Component aComponent) { putClientProperty(NEXT_FOCUS,aComponent); } /** * Return the next focusable component or null if the focus manager * should choose the next focusable component automatically */ public Component getNextFocusableComponent() { return (Component) getClientProperty(NEXT_FOCUS); } /** * Set whether the receiving component can obtain the focus by * calling requestFocus. The default value is true. * Note: Setting this property to false will not prevent the focus * manager from setting the focus to this component, it will prevent * the component from getting the focus when the focus is requested * explicitly. Override isFocusTraversable and return false if the * component should never get the focus. * @beaninfo * expert: true * description: Whether the component can obtain the focus by calling requestFocus. */ public void setRequestFocusEnabled(boolean aFlag) { setFlag(REQUEST_FOCUS_DISABLED,(aFlag ? false:true)); } /** Return whether the receiving component can obtain the focus by * calling requestFocus * @see #setRequestFocusEnabled() */ public boolean isRequestFocusEnabled() { return (getFlag(REQUEST_FOCUS_DISABLED) ? false : true); } /** Set focus on the receiving component if isRequestFocusEnabled returns true **/ public void requestFocus() { if(isRequestFocusEnabled()) { super.requestFocus(); } } /** Set the focus on the receiving component. This method is for focus managers, you * rarely want to call this method, use requestFocus() enstead. */ public void grabFocus() { super.requestFocus(); } /** * Set the preferred size of the receiving component. * if preferredSize is null, the UI will * be asked for the preferred size * @beaninfo * preferred: true * description: The preferred size of the component. */ public void setPreferredSize(Dimension preferredSize) { this.preferredSize = preferredSize; } /** * If the preferredSize has been set to a non-null value * just return it. If the UI delegates getPreferredSize() * method returns a non null then value return that, otherwise * defer to the components layout manager. * * @return the value of the preferredSize property. * @see #setPreferredSize */ public Dimension getPreferredSize() { if (preferredSize != null) { return preferredSize; } Dimension size = null; if (ui != null) { size = ui.getPreferredSize(this); } return (size != null) ? size : super.getPreferredSize(); } /** * Sets the maximumSize of this component to a constant * value. Subsequent calls to getMaximumSize will always * return this value, the components UI will not be asked * to compute it. Setting the maximumSize to null * restores the default behavior. * * @see #getMaximumSize * @beaninfo * preferred: true * description: The maximum size of the component. */ public void setMaximumSize(Dimension maximumSize) { this.maximumSize = maximumSize; } /** * If the maximumSize has been set to a non-null value * just return it. If the UI delegates getMaximumSize() * method returns a non null value then return that, otherwise * defer to the components layout manager. * * @return the value of the maximumSize property. * @see #setMaximumSize */ public Dimension getMaximumSize() { if (maximumSize != null) { return maximumSize; } Dimension size = null; if (ui != null) { size = ui.getMaximumSize(this); } return (size != null) ? size : super.getMaximumSize(); } /** * Sets the minimumSize of this component to a constant * value. Subsequent calls to getMinimumSize will always * return this value, the components UI will not be asked * to compute it. Setting the minimumSize to null * restores the default behavior. * * @see #getMinimumSize * @beaninfo * preferred: true * description: The minimum size of the component. */ public void setMinimumSize(Dimension minimumSize) { this.minimumSize = minimumSize; } /** * If the minimumSize has been set to a non-null value * just return it. If the UI delegates getMinimumSize() * method returns a non null value then return that, otherwise * defer to the components layout manager. * * @return the value of the minimumSize property. * @see #setMinimumSize */ public Dimension getMinimumSize() { if (minimumSize != null) { return minimumSize; } Dimension size = null; if (ui != null) { size = ui.getMinimumSize(this); } return (size != null) ? size : super.getMinimumSize(); } /** * Give the UI delegate an opportunity to define the precise * shape of this component for the sake of mouse processing. * * @return true if this component logically contains x,y. * @see java.awt.Component#contains(int, int) */ public boolean contains(int x, int y) { return (ui != null) ? ui.contains(this, x, y) : super.contains(x, y); } /** * Sets the border of this component. The Border object is * responsible for defining the insets for the component * (overriding any insets set directly on the component) and * for optionally rendering any border decorations within the * bounds of those insets. Borders should be used (rather * than insets) for creating both decorative and non-decorative * (e.g. margins and padding) regions for a swing component. * Compound borders can be used to nest multiple borders within a * single component. *

* This is a bound property. * * @param border the border to be rendered for this component * @see Border * @see CompoundBorder * @beaninfo * bound: true * preferred: true * description: The component's border. */ public void setBorder(Border border) { Border oldBorder = this.border; this.border = border; firePropertyChange("border", oldBorder, border); invalidate(); } /** * Returns the border of this component or null if no border is * currently set. * * @return the border object for this component * @see setBorder */ public Border getBorder() { return border; } /** * If a border has been set on this component, returns the * border's insets, else calls super.getInsets. * * @return the value of the insets property. * @see #setBorder */ public Insets getInsets() { if (border != null) { return border.getBorderInsets(this); } return super.getInsets(); } /** * @return the value of the alignmentY property. * @see #setAlignmentY * @see java.awt.Component#getAlignmentY */ public float getAlignmentY() { return (alignmentY != null) ? alignmentY.floatValue() : super.getAlignmentY(); } /** * Set the value of the alignmentY property. * @see #getAlignmentY * @beaninfo * preferred: true * description: The preferred vertical alignment of the component */ public void setAlignmentY(float alignmentY) { this.alignmentY = new Float(alignmentY > 1.0f ? 1.0f : alignmentY < 0.0f ? 0.0f : alignmentY); } /** * @return the value of the alignmentX property. * @see #setAlignmentX * @see java.awt.Component#getAlignmentX */ public float getAlignmentX() { return (alignmentX != null) ? alignmentX.floatValue() : super.getAlignmentX(); } /** * Set the value of the alignmentX property. * @see #getAlignmentX * @beaninfo * preferred: true * description: The preferred horizontal alignment of the component */ public void setAlignmentX(float alignmentX) { this.alignmentX = new Float(alignmentX > 1.0f ? 1.0f : alignmentX < 0.0f ? 0.0f : alignmentX); } /** * Returns this component's graphics context, which lets you draw * on a component. Use this method get a Graphics object and * then invoke oeprations on that object to draw on the component. */ public Graphics getGraphics() { if (shouldDebugGraphics() != 0) { DebugGraphics graphics = new DebugGraphics(super.getGraphics(), this); return graphics; } return SwingGraphics.createSwingGraphics(super.getGraphics()); } /** Enables or disables diagnostic information about every graphics * operation performed within the component or one of its children. The * value of debugOptions determines how the component should * display this information: *

* debug is bitwise OR'd into the current value. * DebugGraphics.NONE_OPTION disables debugging. * A value of 0 causes no changes to the debugging options. * @beaninfo * preferred: true * description: Diagnostic options for graphics operations. */ public void setDebugGraphicsOptions(int debugOptions) { DebugGraphics.setDebugOptions(this, debugOptions); } /** Returns the state of graphics debugging. * @see #setDebugGraphicsOptions */ public int getDebugGraphicsOptions() { return DebugGraphics.getDebugOptions(this); } /** * Returns true if debug information is enabled for this JComponent * or one if its parents. */ int shouldDebugGraphics() { return DebugGraphics.shouldComponentDebug(this); } /** * Register a new keyboard action. * anAction will be invoked if a key event matching aKeyStroke occurs * and aCondition is verified. The KeyStroke object defines a * particular combination of a keyboard key and one or more modifiers * (alt, shift, ctrl, meta). *

* The aCommand will be set in the delivered event if specified. *

* The Condition can be one of: *

*
*
WHEN_FOCUSED *
The action will be invoked only when the keystroke occurs * while the component has the focus. *
WHEN_IN_FOCUSED_WINDOW *
The action will be invoked when the keystroke occurs while * the component has the focus or if the component is in the * window that has the focus. Note that the component need not * be an immediate descendent of the window -- it can be * anywhere in the window's containment hierarchy. In other * words, whenever any component in the window has the focus, * the action registered with this component is invoked. *
WHEN_ANCESTOR_OF_FOCUSED_COMPONENT *
The action will be invoked when the keystroke occurs while the * component has the focus or if the component is an ancestor of * the component that has the focus. *
*
*

* The combination of keystrokes and conditions lets you define high * level (semantic) action events for a specified keystroke+modifier * combination (using the KeyStroke class) and direct to a parent or * child of a component that has the focus, or to the component itself. * In other words, in any hierarchical structure of components, an * arbitrary key-combination can be immediately directed to the * appropriate component in the hierarchy, and cause a specific method * to be invoked (usually by way of adapter objects). *

* If an action has already been registered for the receiving * container, with the same charCode and the same modifiers, * anAction will replace the action. * * @see KeyStroke */ public void registerKeyboardAction(ActionListener anAction,String aCommand,KeyStroke aKeyStroke,int aCondition) { Hashtable bindings; boolean firstKeyboardAction = false; synchronized(this) { bindings = (Hashtable) getClientProperty(KEYBOARD_BINDINGS_KEY); if(bindings == null) { bindings = new Hashtable(); putClientProperty(KEYBOARD_BINDINGS_KEY,bindings); firstKeyboardAction = true; } } synchronized(bindings) { bindings.put(aKeyStroke,new KeyboardBinding(anAction,aCommand,aKeyStroke,aCondition)); } /* This is the first time a keyboard binding is added, let's order * keyboard events... * ALERT: we need to enable events. Adding a listener will not work since * we want our listener to be after all other listeners. */ if(firstKeyboardAction) { enableEvents(AWTEvent.KEY_EVENT_MASK); } } /** * Calls registerKeyboardAction(ActionListener,String,KeyStroke,condition) with a null command. */ public void registerKeyboardAction(ActionListener anAction,KeyStroke aKeyStroke,int aCondition) { registerKeyboardAction(anAction,null,aKeyStroke,aCondition); } private Hashtable keyboardBindings() { Hashtable bindings; synchronized(this) { bindings = (Hashtable) getClientProperty(KEYBOARD_BINDINGS_KEY); } return bindings; } /** * Unregister a keyboard action. * * @see #registerKeyboardAction */ public void unregisterKeyboardAction(KeyStroke aKeyStroke) { Hashtable bindings = keyboardBindings(); if(bindings == null) return; synchronized(bindings) { bindings.remove(aKeyStroke); } if(bindings.size() == 0) { /** ALERT. We need a way to disable keyboard events only if there is no * keyboard listener. */ } } /** * Return the KeyStrokes that will initiate registered actions. * * @return an array of KeyStroke objects * @see #registerKeyboardAction */ public KeyStroke[] getRegisteredKeyStrokes() { Hashtable bindings = keyboardBindings(); KeyStroke result[]; int i; Enumeration keys; if(bindings == null) return new KeyStroke[0]; synchronized(bindings) { result = new KeyStroke[bindings.size()]; i = 0; keys = bindings.keys(); while(keys.hasMoreElements()) result[i++] = (KeyStroke) keys.nextElement(); } return result; } /** * Return the condition that determines whether a registered action * occurs in response to the specified keystroke. * * @return the action-keystroke condition * @see #registerKeyboardAction */ public int getConditionForKeyStroke(KeyStroke aKeyStroke) { Hashtable bindings = keyboardBindings(); if(bindings == null) return UNDEFINED_CONDITION; synchronized(bindings) { KeyboardBinding kb = (KeyboardBinding) bindings.get(aKeyStroke); if(kb != null) { return kb.getCondition(); } } return UNDEFINED_CONDITION; } /** * Return the object that will perform the action registered for a * given keystroke. * * @return the ActionListener object invoked when the keystroke occurs * @see #registerKeyboardAction */ public ActionListener getActionForKeyStroke(KeyStroke aKeyStroke) { Hashtable bindings = keyboardBindings(); if(bindings == null) return null; synchronized(bindings) { KeyboardBinding kb = (KeyboardBinding) bindings.get(aKeyStroke); if(kb != null) { return kb.getAction(); } } return null; } /** * Unregister all keyboard actions * * @see #registerKeyboardAction */ public void resetKeyboardActions() { synchronized(this) { Hashtable bindings = (Hashtable) getClientProperty(KEYBOARD_BINDINGS_KEY); if(bindings != null) { bindings.clear(); } } /* ALERT. We need a way to disable keyboard events only if there is no * keyboard listener. */ } /** * Request the focus for the component that should have the focus * by default. The default implementation will recursively request * the focus on the first component that is focus-traversable. * * @return false if the focus has not been set, otherwise * return true */ public boolean requestDefaultFocus() { Component ca[] = getComponents(); int i; for(i=0 ; i < ca.length ; i++) { if(ca[i].isFocusTraversable()) { if(ca[i] instanceof JComponent) { ((JComponent)ca[i]).grabFocus(); } else { ca[i].requestFocus(); } return true; } if(ca[i] instanceof JComponent && !((JComponent)ca[i]).isManagingFocus()) { if(((JComponent)(ca[i])).requestDefaultFocus()) { return true; } } } return false; } public void setVisible(boolean aFlag) { if(aFlag != isVisible()) { super.setVisible(aFlag); Container parent = getParent(); if(parent != null) { Rectangle r = getBounds(); parent.repaint(r.x,r.y,r.width,r.height); } } } /** * Identifies whether or not this component can receive the focus. * A disabled button, for example, would return false. * * @return true if this component can receive the focus */ public boolean isFocusTraversable() { boolean result = false; Hashtable bindings; synchronized(this) { bindings = (Hashtable) getClientProperty(KEYBOARD_BINDINGS_KEY); } if(bindings != null) { synchronized(bindings) { Enumeration keys = bindings.keys(); KeyboardBinding b; while(keys.hasMoreElements()) { b = (KeyboardBinding) bindings.get(keys.nextElement()); if(b.getCondition() == WHEN_FOCUSED) { result = true; break; } } } } return result; } protected void processFocusEvent(FocusEvent e) { switch(e.getID()) { case FocusEvent.FOCUS_GAINED: setFlag(HAS_FOCUS, true); break; case FocusEvent.FOCUS_LOST: setFlag(HAS_FOCUS, false); break; } // Call super *after* setting flag, in case listener calls paint. super.processFocusEvent(e); } /** * Process any key events that the component itself * recognizes. This will be called after the focus * manager and any interested listeners have been * given a chance to steal away the event. This * method will only be called is the event has not * yet been consumed. This method is called prior * to the keyboard UI logic. *

* This is implemented to do nothing. Subclasses would * normally override this method if they process some * key events themselves. If the event is processed, * it should be consumed. */ protected void processComponentKeyEvent(KeyEvent e) { } /** Override processKeyEvent to process events **/ protected void processKeyEvent(KeyEvent e) { // focus manager gets to steal the event if it wants it. boolean result; boolean shouldProcessKey = false; if(FocusManager.isFocusManagerEnabled()) { FocusManager focusManager = FocusManager.getCurrentManager(); focusManager.processKeyEvent(this,e); if(e.isConsumed()) { return; } } // This gives the key event listeners a crack at the event super.processKeyEvent(e); // give the component itself a crack at the event if (! e.isConsumed()) { processComponentKeyEvent(e); } if(e.getID() == KeyEvent.KEY_PRESSED) { shouldProcessKey = true; if(!KeyboardState.keyIsPressed(e.getKeyCode())) KeyboardState.registerKeyPressed(e.getKeyCode()); } else if(e.getID() == KeyEvent.KEY_RELEASED) { if(KeyboardState.keyIsPressed(e.getKeyCode())) { shouldProcessKey = true; KeyboardState.registerKeyReleased(e.getKeyCode()); } } else if(e.getID() == KeyEvent.KEY_TYPED) { shouldProcessKey = true; } if(e.isConsumed()) { return; } if(shouldProcessKey && e.getID() == KeyEvent.KEY_PRESSED) { result = processKeyBindings(e,true); if(result) e.consume(); } else if(shouldProcessKey && e.getID() == KeyEvent.KEY_RELEASED) { result = processKeyBindings(e,false); if(result) { e.consume(); } } else if(shouldProcessKey && e.getID() == KeyEvent.KEY_TYPED) { result = processKeyBindings(e,false); if(result) { e.consume(); } } } KeyboardBinding bindingForKeyStroke(KeyStroke ks,int condition) { Hashtable bindings; KeyboardBinding b; KeyboardBinding result = null; // synchronized(this) { bindings = (Hashtable) getClientProperty(KEYBOARD_BINDINGS_KEY); // } if(bindings != null) { // synchronized(bindings) { b = (KeyboardBinding) bindings.get(ks); // System.out.println("Bindings are " + bindings); if(b != null) { ActionListener action = b.getAction(); if((action instanceof Action) && !(((Action)action).isEnabled())) action = null; if(action != null) { switch(b.getCondition()) { case WHEN_FOCUSED: if(condition == WHEN_FOCUSED) result = b; break; case WHEN_ANCESTOR_OF_FOCUSED_COMPONENT: if(condition == WHEN_FOCUSED || condition == WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) result = b; break; case WHEN_IN_FOCUSED_WINDOW: if(condition == WHEN_FOCUSED || condition == WHEN_IN_FOCUSED_WINDOW || condition == WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) result = b; break; } } } // } } return result; } boolean processKeyBinding(KeyEvent e,int condition,boolean pressed) { Hashtable bindings; int i,c; boolean onKeyRelease = (pressed?false:true); KeyboardBinding binding = null; KeyStroke ks; if(isEnabled()) { if(e.getID() == KeyEvent.KEY_TYPED) { binding = bindingForKeyStroke((ks=KeyStroke.getKeyStroke(e.getKeyChar())),condition); } else { binding = bindingForKeyStroke((ks=KeyStroke.getKeyStroke(e.getKeyCode(),e.getModifiers(), onKeyRelease)), condition); } //System.out.println("e=" + e + "ks is " + ks); if(binding != null) { ActionListener listener = binding.getAction(); if(listener != null) { listener.actionPerformed(new ActionEvent(this,ActionEvent.ACTION_PERFORMED,binding.getCommand())); return true; } } } return false; } boolean processKeyBindings(KeyEvent e,boolean pressed) { Container parent; Vector processedComponents = new Vector(); /* Do we have a key binding for e? */ if(processKeyBinding(e,WHEN_FOCUSED,pressed)) return true; processedComponents.addElement(this); /* We have no key binding. Let's try the path from our parent to the window excluded * We store the path components so we can avoid asking the same component twice. */ parent = this.getParent(); while(parent != null && !(parent instanceof Window) && !(parent instanceof Applet)) { if(parent instanceof JComponent) { if(((JComponent)parent).processKeyBinding(e,WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,pressed)) return true; processedComponents.addElement(parent); } parent = parent.getParent(); } /* No components between the focused component and the window is actually interested * by the key event. Let's try the other JComponent in this window. */ if(parent != null) { return JComponent.processKeyBindingsForAllComponents(e,parent,processedComponents,pressed); } return false; } static boolean processKeyBindingsForAllComponents(KeyEvent e,Container container,Vector alreadyProcessed, boolean pressed) { int i; Component subComponents[]; if(!container.isVisible() || !container.isEnabled()) { return false; } if(container instanceof JComponent && !alreadyProcessed.contains(container)) { if(((JComponent)container).processKeyBinding(e,WHEN_IN_FOCUSED_WINDOW,pressed)) return true; } subComponents = container.getComponents(); for(i=0 ; i < subComponents.length ; i++) { if(subComponents[i].isVisible() && subComponents[i].isEnabled()) { if(subComponents[i] instanceof Container) { if(processKeyBindingsForAllComponents(e,(Container)subComponents[i],alreadyProcessed,pressed)) return true; } } } return false; } /** * Registers the text to display in a tool tip. * The text displays when the cursor lingers over the component. *

* See How to Use Tool Tips * in The Java Tutorial * for further documentation. * * @param text The string to display. If the text is null, * the tool tip is turned off for this component. * @see #TOOL_TIP_TEXT_KEY * @beaninfo * preferred: true * description: The text to display in a tool tip. */ public void setToolTipText(String text) { putClientProperty(TOOL_TIP_TEXT_KEY, text); ToolTipManager toolTipManager = ToolTipManager.sharedInstance(); if (text != null) { toolTipManager.registerComponent(this); } else { toolTipManager.unregisterComponent(this); } } /** * Return the tooltip string that has been set with setToolTipText() * * @return the text of the tool tip * @see #TOOL_TIP_TEXT_KEY */ public String getToolTipText() { return (String)getClientProperty(TOOL_TIP_TEXT_KEY); } /** * Returns the string to be used as the tooltip for event. By default * this returns any string set using setToolTipText(). If a component provides * more extensize API to support differing tooltips at different locations, * this method should be overridden. */ public String getToolTipText(MouseEvent event) { return getToolTipText(); } /** * Return the tooltip location in the receiving component coordinate system * If null is returned, Swing will choose a location. * The default implementation returns null. * * @param event the MouseEvent that caused the ToolTipManager to * show the tooltip. */ public Point getToolTipLocation(MouseEvent event) { return null; } /** * Returns the instance of JToolTip that should be used to display the tooltip. * Components typically would not override this method, but it can be used to * cause different tooltips to be displayed differently. */ public JToolTip createToolTip() { JToolTip tip = new JToolTip(); tip.setComponent(this); return tip; } /** * Forwards the scrollRectToVisible() message to the JComponent's * parent. Components that can service the request, such as a JViewport, * override this method and perform the scrolling. * * @see JViewport */ public void scrollRectToVisible(Rectangle aRect) { Container parent; int dx = getX(), dy = getY(); for (parent = getParent(); !(parent == null) && !(parent instanceof JComponent) && !(parent instanceof CellRendererPane); parent = parent.getParent()) { Rectangle bounds = parent.getBounds(); dx += bounds.x; dy += bounds.y; } if (!(parent == null) && !(parent instanceof CellRendererPane)) { aRect.x += dx; aRect.y += dy; ((JComponent)parent).scrollRectToVisible(aRect); aRect.x -= dx; aRect.y -= dy; } } /** * If true this component will automatically scroll its contents when * dragged, if contained in a component that supports scrolling, such as * JViewport * * @see JViewport * @see #getAutoscrolls * * @beaninfo * expert: true * description: Whether this component automatically scrolls its contents when dragged. */ public void setAutoscrolls(boolean autoscrolls) { if (autoscrolls) { if (autoscroller == null) { autoscroller = new Autoscroller(this); } } else { if (autoscroller != null) { autoscroller.stop(); autoscroller = null; } } } /** * Returns true if this component automatically scrolls its * contents when dragged, (when contained in a component that supports * scrolling, like JViewport * * @see JViewport * @see #setAutoscrolls */ public boolean getAutoscrolls() { return autoscroller != null; } protected void processMouseMotionEvent(MouseEvent e) { boolean dispatch = true; if (autoscroller != null) { if (e.getID() == MouseEvent.MOUSE_DRAGGED) { // We don't want to do the drags when the mouse moves if we're // autoscrolling. It makes it feel spastic. dispatch = !autoscroller.timer.isRunning(); autoscroller.mouseDragged(e); } } if (dispatch) { super.processMouseMotionEvent(e); } } // Inner classes can't get at this method from a super class void superProcessMouseMotionEvent(MouseEvent e) { super.processMouseMotionEvent(e); } static class KeyboardBinding implements Serializable { ActionListener action; String command; KeyStroke keyStroke; int condition; KeyboardBinding(ActionListener action,String aCommand,KeyStroke aKeyStroke,int condition) { this.action = action; this.command = aCommand; this.keyStroke = aKeyStroke; this.condition = condition; } ActionListener getAction() { return action; } String getCommand() { return command; } KeyStroke getKeyStroke() { return keyStroke; } int getCondition() { return condition; } public String toString() { return "KeyBinding ("+action+","+keyStroke+","+condition+")"; } } // This class is used by the KeyboardState class to provide a single // instance which can be stored in the AppContext. static final class IntVector { int array[] = null; int count = 0; int capacity = 0; int size() { return count; } int elementAt(int index) { return array[index]; } void addElement(int value) { if (count == capacity) { capacity = (capacity + 2) * 2; int[] newarray = new int[capacity]; if (count > 0) { System.arraycopy(array, 0, newarray, 0, count); } array = newarray; } array[count++] = value; } void setElementAt(int value, int index) { array[index] = value; } } static class KeyboardState implements Serializable { private static final Object keyCodesKey = JComponent.KeyboardState.class; // Get the array of key codes from the AppContext. static IntVector getKeyCodeArray() { IntVector iv = (IntVector)SwingUtilities.appContextGet(keyCodesKey); if (iv == null) { iv = new IntVector(); SwingUtilities.appContextPut(keyCodesKey, iv); } return iv; } static void registerKeyPressed(int keyCode) { IntVector kca = getKeyCodeArray(); int count = kca.size(); int i; for(i=0;i * Warning: serialized objects of this class will not be compatible with * future swing releases. The current serialization support is appropriate * for short term storage or RMI between Swing1.0 applications. It will * not be possible to load serialized Swing1.0 objects with future releases * of Swing. The JDK1.2 release of Swing will be the compatibility * baseline for the serialized form of Swing objects. */ public abstract class AccessibleJComponent extends AccessibleContext implements Serializable, AccessibleComponent { // AccessibleContext methods // /** * Get the accessible name of this object. This should almost never * return java.awt.Component.getName(), as that generally isn't * a localized name, and doesn't have meaning for the user. If the * object is fundamentally a text object (e.g. a menu item), the * accessible name should be the text of the object (e.g. "save"). * If the object has a tooltip, the tooltip text may also be an * appropriate String to return. * * @return the localized name of the object -- can be null if this * object does not have a name * @see AccessibleContext#setAccessibleName */ public String getAccessibleName() { if (accessibleName != null) { return accessibleName; } else { Border border = getBorder(); if ((border != null) && (border instanceof TitledBorder)) { return ((TitledBorder) border).getTitle(); } } return null; } /** * Get the accessible description of this object. This should be * a concise, localized description of what this object is - what * is it's meaning to the user. If the object has a tooltip, the * tooltip text may be an appropriate string to return, assuming * it contains a concise description of the object (instead of just * the name of the object - e.g. a "Save" icon on a toolbar that * had "save" as the tooltip text shouldn't return the tooltip * text as the description, but something like "Saves the current * text document" instead). * * @return the localized description of the object -- can be null if * this object does not have a description * @see AccessibleContext#setAccessibleDescription */ public String getAccessibleDescription() { if (accessibleDescription != null) { return accessibleDescription; } else { try { String ttt = getToolTipText(null); if (ttt != null) { return ttt; } } catch (Exception e) { // Just in case the subclass overrode the // getToolTipText method and actually // requires a MouseEvent. // [[[FIXME: WDW - we probably should require this // method to take a MouseEvent and just pass it on // to getToolTipText. The swing-feedback traffic // leads me to believe getToolTipText might change, // though, so I was hesitant to make this change at // this time.]]] } } return null; } /** * Get the role of this object. * * @return an instance of AccessibleRole describing the role of the * object * @see AccessibleRole */ public AccessibleRole getAccessibleRole() { return AccessibleRole.SWING_COMPONENT; } /** * Get the state of this object. * * @return an instance of AccessibleStateSet containing the current * state set of the object * @see AccessibleState */ public AccessibleStateSet getAccessibleStateSet() { return SwingUtilities.getAccessibleStateSet(JComponent.this); } /** * Get the Accessible parent of this object. If the parent of this * object implements Accessible, this method should simply return * getParent(). * * @return the Accessible parent of this object -- can be null if this * object does not have an Accessible parent */ public Accessible getAccessibleParent() { if (accessibleParent != null) { return accessibleParent; } else { Container parent = getParent(); if (parent instanceof Accessible) { return (Accessible) parent; } } return null; } /** * Get the index of this object in its accessible parent. * * @return the index of this object in its parent; -1 if this * object does not have an accessible parent. * @see #getAccessibleParent */ public int getAccessibleIndexInParent() { return SwingUtilities.getAccessibleIndexInParent(JComponent.this); } /** * Returns the number of accessible children in the object. If all * of the children of this object implement Accessible, than this * method should return the number of children of this object. * * @return the number of accessible children in the object. */ public int getAccessibleChildrenCount() { return SwingUtilities.getAccessibleChildrenCount(JComponent.this); } /** * Return the nth Accessible child of the object. * * @param i zero-based index of child * @return the nth Accessible child of the object */ public Accessible getAccessibleChild(int i) { return SwingUtilities.getAccessibleChild(JComponent.this,i); } /** * Return the locale of this object. * * @return the locale of this object */ public Locale getLocale() { return JComponent.this.getLocale(); } /** * Get the AccessibleComponent associated with this object if one * exists. Otherwise return null. */ public AccessibleComponent getAccessibleComponent() { return this; } // AccessibleComponent methods // /** * Get the background color of this object. * * @return the background color, if supported, of the object; * otherwise, null */ public Color getBackground() { return JComponent.this.getBackground(); } // NOTE: IN THE NEXT MAJOR RELEASE, isOpaque WILL MIGRATE // TO java.awt.Component -- ADJUST @SEE LINK BELOW. /** * Set the background color of this object. * (For transparency, see isOpaque.) * * @param c the new Color for the background * @see JComponent#isOpaque */ public void setBackground(Color c) { JComponent.this.setBackground(c); } /** * Get the foreground color of this object. * * @return the foreground color, if supported, of the object; * otherwise, null */ public Color getForeground() { return JComponent.this.getForeground(); } /** * Set the foreground color of this object. * * @param c the new Color for the foreground */ public void setForeground(Color c) { JComponent.this.setForeground(c); } /** * Get the Cursor of this object. * * @return the Cursor, if supported, of the object; otherwise, null */ public Cursor getCursor() { return JComponent.this.getCursor(); } /** * Set the Cursor of this object. * * @param c the new Cursor for the object */ public void setCursor(Cursor cursor) { JComponent.this.setCursor(cursor); } /** * Get the Font of this object. * * @return the Font,if supported, for the object; otherwise, null */ public Font getFont() { return JComponent.this.getFont(); } /** * Set the Font of this object. * * @param f the new Font for the object */ public void setFont(Font f) { JComponent.this.setFont(f); } /** * Get the FontMetrics of this object. * * @param f the Font * @return the FontMetrics, if supported, the object; otherwise, null * @see #getFont */ public FontMetrics getFontMetrics(Font f) { return JComponent.this.getFontMetrics(f); } /** * Determine if the object is enabled. * * @return true if object is enabled; otherwise, false */ public boolean isEnabled() { return JComponent.this.isEnabled(); } /** * Set the enabled state of the object. * * @param b if true, enables this object; otherwise, disables it */ public void setEnabled(boolean b) { boolean old = JComponent.this.isEnabled(); JComponent.this.setEnabled(b); if (b != old) { if (accessibleContext != null) { if (b) { accessibleContext.firePropertyChange( AccessibleContext.ACCESSIBLE_STATE_PROPERTY, null, AccessibleState.ENABLED); } else { accessibleContext.firePropertyChange( AccessibleContext.ACCESSIBLE_STATE_PROPERTY, AccessibleState.ENABLED, null); } } } } /** * Determine if the object is visible. Note: this means that the * object intends to be visible; however, it may not in fact be * showing on the screen because one of the objects that this object * is contained by is not visible. To determine if an object is * showing on the screen, use isShowing(). * * @return true if object is visible; otherwise, false */ public boolean isVisible() { return JComponent.this.isVisible(); } /** * Set the visible state of the object. * * @param b if true, shows this object; otherwise, hides it */ public void setVisible(boolean b) { boolean old = JComponent.this.isVisible(); JComponent.this.setVisible(b); if (b != old) { if (accessibleContext != null) { if (b) { accessibleContext.firePropertyChange( AccessibleContext.ACCESSIBLE_STATE_PROPERTY, null, AccessibleState.VISIBLE); } else { accessibleContext.firePropertyChange( AccessibleContext.ACCESSIBLE_STATE_PROPERTY, AccessibleState.VISIBLE, null); } } } } /** * Determine if the object is showing. This is determined by checking * the visibility of the object and ancestors of the object. Note: * this will return true even if the object is obscured by another * (for example, it happens to be underneath a menu that was pulled * down). * * @return true if object is showing; otherwise, false */ public boolean isShowing() { return JComponent.this.isShowing(); } /** * Checks whether the specified point is within this object's bounds, * where the point's x and y coordinates are defined to be relative to * the coordinate system of the object. * * @param p the Point relative to the coordinate system of the object * @return true if object contains Point; otherwise false */ public boolean contains(Point p) { return JComponent.this.contains(p); } /** * Returns the location of the object on the screen. * * @return location of object on screen -- can be null if this object * is not on the screen */ public Point getLocationOnScreen() { if (JComponent.this.isShowing()) { return JComponent.this.getLocationOnScreen(); } else { return null; } } /** * Gets the location of the object relative to the parent in the form * of a point specifying the object's top-left corner in the screen's * coordinate space. * * @return An instance of Point representing the top-left corner of * the objects's bounds in the coordinate space of the screen; null if * this object or its parent are not on the screen */ public Point getLocation() { return JComponent.this.getLocation(); } /** * Sets the location of the object relative to the parent. */ public void setLocation(Point p) { JComponent.this.setLocation(p); } /** * Gets the bounds of this object in the form of a Rectangle object. * The bounds specify this object's width, height, and location * relative to its parent. * * @return A rectangle indicating this component's bounds; null if * this object is not on the screen. */ public Rectangle getBounds() { return JComponent.this.getBounds(); } /** * Sets the bounds of this object in the form of a Rectangle object. * The bounds specify this object's width, height, and location * relative to its parent. * * @param A rectangle indicating this component's bounds */ public void setBounds(Rectangle r) { JComponent.this.setBounds(r); } /** * Returns the size of this object in the form of a Dimension object. * The height field of the Dimension object contains this objects's * height, and the width field of the Dimension object contains this * object's width. * * @return A Dimension object that indicates the size of this * component; null if this object is not on the screen */ public Dimension getSize() { return JComponent.this.getSize(); } /** * Resizes this object so that it has width width and height. * * @param d - The dimension specifying the new size of the object. */ public void setSize(Dimension d) { JComponent.this.setSize(d); } /** * Returns the Accessible child, if one exists, contained at the local * coordinate Point. * * @param p The point defining the top-left corner of the Accessible, * given in the coordinate space of the object's parent. * @return the Accessible, if it exists, at the specified location; * else null */ public Accessible getAccessibleAt(Point p) { return SwingUtilities.getAccessibleAt(JComponent.this, p); } /** * Returns whether this object can accept focus or not. * * @return true if object can accept focus; otherwise false */ public boolean isFocusTraversable() { return JComponent.this.isFocusTraversable(); } /** * Requests focus for this object. */ public void requestFocus() { JComponent.this.requestFocus(); } /** * Adds the specified focus listener to receive focus events from this * component. * * @param l the focus listener */ public void addFocusListener(FocusListener l) { JComponent.this.addFocusListener(l); } /** * Removes the specified focus listener so it no longer receives focus * events from this component. * * @param l the focus listener */ public void removeFocusListener(FocusListener l) { JComponent.this.removeFocusListener(l); } } // inner class AccessibleJComponent /** * @return a small Hashtable * @see #putClientProperty * @see #getClientProperty */ private Dictionary getClientProperties() { if (clientProperties == null) { clientProperties = new Hashtable(2); } return clientProperties; } /** * Returns the value of the property with the specified key. Only * properties added with putClientProperty will return * a non-null value. * * @return the value of this property or null * @see #putClientProperty */ public final Object getClientProperty(Object key) { return getClientProperties().get(key); } /** * Add an arbitrary key/value "client property" to this component. *

* The get/putClientProperty methods provide access to * a small per-instance hashtable. Callers can use get/putClientProperty * to annotate components that were created by another module, e.g. a * layout manager might store per child constraints this way. For example: *

     * componentA.putClientProperty("to the left of", componentB);
     * 
*

* If value is null this method will remove the property. * Changes to client properties are reported with PropertyChange * events. The name of the property (for the sake of PropertyChange * events) is key.toString(). *

* The clientProperty dictionary is not intended to support large * scale extensions to JComponent nor should be it considered an * alternative to subclassing when designing a new component. * * @see #getClientProperty * @see #addPropertyChangeListener */ public final void putClientProperty(Object key, Object value) { Object oldValue = getClientProperties().get(key); if (value != null) { getClientProperties().put(key, value); } else { getClientProperties().remove(key); } firePropertyChange(key.toString(), oldValue, value); } /* --- Transitional java.awt.Component Support --- * * The methods and fields in this section will migrate to * java.awt.Component in the next JDK release. * */ private PropertyChangeSupport changeSupport; /** * Returns true if this component is a lightweight, i.e. if it doesn't * have a native window system peer. *

* This method will migrate to java.awt.Component in the next major JDK release * * @return true if this component is a lightweight */ public static boolean isLightweightComponent(Component c) { return c.getPeer() instanceof java.awt.peer.LightweightPeer; } /** * Moves and resizes this component. * * @see java.awt.Component#setBounds */ public void reshape(int x, int y, int w, int h) { if(isShowing()) { /* If there is an intersection between the new bounds and the old * one, refresh only the visible rects */ if(!((_bounds.x + _bounds.width <= x) || (_bounds.y + _bounds.height <= y) || (_bounds.x >= (x + w)) || (_bounds.y >= (y + h)))) { Rectangle[] rev = SwingUtilities.computeDifference(getBounds(), new Rectangle(x,y,w,h)); int i,c; Container parent = getParent(); for(i=0,c=rev.length ; i < c ; i++) { parent.repaint(rev[i].x,rev[i].y,rev[i].width,rev[i].height); // System.out.println("Repaint " + rev[i]); } } else { getParent().repaint(_bounds.x,_bounds.y,_bounds.width,_bounds.height); } } _bounds.setBounds(x, y, w, h); super.reshape(x, y, w, h); } /** * Moves and resizes this component. * * @see java.awt.Component#setBounds */ public void setBounds(Rectangle r) { _bounds.setBounds(r); super.setBounds(r); } /** * Store the bounds of this component into "return value" rv and * return rv. If rv is null a new Rectangle is allocated. * This version of getBounds() is useful if the caller * wants to avoid allocating a new Rectangle object on the heap. * * @param rv the return value, modified to the components bounds * @return rv */ public Rectangle getBounds(Rectangle rv) { if (rv == null) { return new Rectangle(getX(), getY(), getWidth(), getHeight()); } else { rv.setBounds(getX(), getY(), getWidth(), getHeight()); return rv; } } /** * Store the width/height of this component into "return value" rv * and return rv. If rv is null a new Dimension object is * allocated. This version of getSize() is useful if the * caller wants to avoid allocating a new Dimension object on the heap. * * @param rv the return value, modified to the components size * @return rv */ public Dimension getSize(Dimension rv) { if (rv == null) { return new Dimension(getWidth(), getHeight()); } else { rv.setSize(getWidth(), getHeight()); return rv; } } /** * Store the x,y origin of this component into "return value" rv * and return rv. If rv is null a new Point is allocated. * This version of getLocation() is useful if the * caller wants to avoid allocating a new Point object on the heap. * * @param rv the return value, modified to the components location * @return rv */ public Point getLocation(Point rv) { if (rv == null) { return new Point(getX(), getY()); } else { rv.setLocation(getX(), getY()); return rv; } } /** * Return the current x coordinate of the components origin. * This method is preferable to writing component.getBounds().x, * or component.getLocation().x because it doesn't cause any * heap allocations. *

* This method will migrate to java.awt.Component in the next major JDK release * * @return the current x coordinate of the components origin. */ public int getX() { return _bounds.x; } /** * Return the current y coordinate of the components origin. * This method is preferable to writing component.getBounds().y, * or component.getLocation().y because it doesn't cause any * heap allocations. *

* This method will migrate to java.awt.Component in the next major JDK release * * @return the current y coordinate of the components origin. */ public int getY() { return _bounds.y; } /** * Return the current width of this component. * This method is preferable to writing component.getBounds().width, * or component.getSize().width because it doesn't cause any * heap allocations. *

* This method will migrate to java.awt.Component in the next major JDK release * * @return the current width of this component. */ public int getWidth() { return _bounds.width; } /** * Return the current height of this component. * This method is preferable to writing component.getBounds().height, * or component.getSize().height because it doesn't cause any * heap allocations. *

* This method will migrate to java.awt.Component in the next major JDK release * * @return the current height of this component. */ public int getHeight() { return _bounds.height; } /** * Returns true if this Component has the keyboard focus. *

* This method will migrate to java.awt.Component in the next major JDK release * * @return true if this Component has the keyboard focus. */ public boolean hasFocus() { return getFlag(HAS_FOCUS); } /** * Returns true if this component is completely opaque. *

* An opaque component paints every pixel within its * rectangular region. A non-opaque component paints only some of * its pixels, allowing the pixels underneath it to "show through". * A component that does not fully paint its pixels therefore * provides a degree of transparency. *

* Subclasses that guarantee to always completely paint their contents should * override this method and return true. *

* This method will migrate to java.awt.Component in the next major JDK release * * @return true if this component is completely opaque. * @see #setOpaque */ public boolean isOpaque() { return getFlag(IS_OPAQUE); } /** * If true the components background will be filled with the * background color. Otherwise, the background is transparent, * and whatever is underneath will show through. *

* The default value of this property is false. *

* This is a JavaBeans bound property. * * @see #isOpaque */ public void setOpaque(boolean isOpaque) { boolean oldValue = getFlag(IS_OPAQUE); setFlag(IS_OPAQUE, isOpaque); firePropertyChange("opaque", oldValue, isOpaque); } /** * If the specified retangle is completely obscured by any of this * components opaque children then return true. Only direct children * are considered, more distant descendants are ignored. A JComponent * is opaque if JComponent.isOpaque() returns true, other lightweight * components are always considered transparent, and heavyweight components * are always considered opaque. * * @return true if the specified rectangle is obscured by an opaque child */ boolean rectangleIsObscured(int x,int y,int width,int height) { Component children[] = getComponents(); if(children == null) { return false; } for(int i = 0; i < children.length; i++) { Component child = children[i]; Rectangle childBounds; if (child instanceof JComponent) { childBounds = ((JComponent)child)._bounds; } else { childBounds = child.getBounds(); } if (x >= childBounds.x && (x + width) <= (childBounds.x + childBounds.width) && y >= childBounds.y && (y + height) <= (childBounds.y + childBounds.height)) { if(child instanceof JComponent) { return ((JComponent)child).isOpaque(); } else { /** Sometimes an heavy weight can have a bound larger than it's peer size * so we should always draw under heavy weights */ return false; } } } return false; } /** * Returns the Component's "visible rect rectangle" - the * intersection of the visible rectangles for this component * and all of its ancestors. The return value is stored in * visibleRect * * @see #getVisibleRect */ static final void computeVisibleRect(Component c, Rectangle visibleRect) { Container p = c.getParent(); Rectangle bounds = c.getBounds(); if (p == null || p instanceof Window || p instanceof Applet) { visibleRect.setBounds(0, 0, bounds.width, bounds.height); } else { computeVisibleRect(p, visibleRect); visibleRect.x -= bounds.x; visibleRect.y -= bounds.y; SwingUtilities.computeIntersection(0,0,bounds.width,bounds.height,visibleRect); } } /** * Returns the Component's "visible rect rectangle" - the * intersection of the visible rectangles for this component * and all of its ancestors. The return value is stored in * visibleRect * * @see #getVisibleRect */ public void computeVisibleRect(Rectangle visibleRect) { computeVisibleRect(this, visibleRect); } /** * Returns the Component's "visible rectangle" - the * intersection of this components visible rectangle: *

     * new Rectangle(0, 0, getWidth(), getHeight());
     * 
* and all of its ancestors visible Rectangles. * * @return the visible rectangle */ public Rectangle getVisibleRect() { Rectangle visibleRect = new Rectangle(); computeVisibleRect(visibleRect); return visibleRect; } /** * Support for reporting bound property changes. If oldValue and * newValue are not equal and the PropertyChangeEvent listener list * isn't empty, then fire a PropertyChange event to each listener. * This method has an overloaded method for each primitive type. For * example, here's how to write a bound property set method whose * value is an int: *
     * public void setFoo(int newValue) {
     *     int oldValue = foo;
     *     foo = newValue;
     *     firePropertyChange("foo", oldValue, newValue);
     * }
     * 
*

* This method will migrate to java.awt.Component in the next major JDK release * * @param propertyName The programmatic name of the property that was changed. * @param oldValue The old value of the property. * @param newValue The new value of the property. * @see java.beans.PropertyChangeSupport */ protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) { if (changeSupport != null) { changeSupport.firePropertyChange(propertyName, oldValue, newValue); } } /* * PENDING(hmuller) in JDK1.2 the following firePropertyChange overloads * should additional check for a non-empty listener list with * changeSupport.hasListeners(propertyName) before calling firePropertyChange. */ /** * @see #firePropertyChange(java.lang.String, java.lang.Object, java.lang.Object) */ public void firePropertyChange(String propertyName, byte oldValue, byte newValue) { if ((changeSupport != null) && (oldValue != newValue)) { changeSupport.firePropertyChange(propertyName, new Byte(oldValue), new Byte(newValue)); } } /** * @see #firePropertyChange(java.lang.String, java.lang.Object, java.lang.Object) */ public void firePropertyChange(String propertyName, char oldValue, char newValue) { if ((changeSupport != null) && (oldValue != newValue)) { changeSupport.firePropertyChange(propertyName, new Character(oldValue), new Character(newValue)); } } /** * @see #firePropertyChange(java.lang.String, java.lang.Object, java.lang.Object) */ public void firePropertyChange(String propertyName, short oldValue, short newValue) { if ((changeSupport != null) && (oldValue != newValue)) { changeSupport.firePropertyChange(propertyName, new Short(oldValue), new Short(newValue)); } } /** * @see #firePropertyChange(java.lang.String, java.lang.Object, java.lang.Object) */ public void firePropertyChange(String propertyName, int oldValue, int newValue) { if ((changeSupport != null) && (oldValue != newValue)) { changeSupport.firePropertyChange(propertyName, new Integer(oldValue), new Integer(newValue)); } } /** * @see #firePropertyChange(java.lang.String, java.lang.Object, java.lang.Object) */ public void firePropertyChange(String propertyName, long oldValue, long newValue) { if ((changeSupport != null) && (oldValue != newValue)) { changeSupport.firePropertyChange(propertyName, new Long(oldValue), new Long(newValue)); } } /** * @see #firePropertyChange(java.lang.String, java.lang.Object, java.lang.Object) */ public void firePropertyChange(String propertyName, float oldValue, float newValue) { if ((changeSupport != null) && (oldValue != newValue)) { changeSupport.firePropertyChange(propertyName, new Float(oldValue), new Float(newValue)); } } /** * @see #firePropertyChange(java.lang.String, java.lang.Object, java.lang.Object) */ public void firePropertyChange(String propertyName, double oldValue, double newValue) { if ((changeSupport != null) && (oldValue != newValue)) { changeSupport.firePropertyChange(propertyName, new Double(oldValue), new Double(newValue)); } } /** * @see #firePropertyChange(java.lang.String, java.lang.Object, java.lang.Object) */ public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) { if ((changeSupport != null) && (oldValue != newValue)) { changeSupport.firePropertyChange(propertyName, new Boolean(oldValue), new Boolean(newValue)); } } /** * Add a PropertyChangeListener to the listener list. * The listener is registered for all properties. *

* A PropertyChangeEvent will get fired in response to setting * a bound property, e.g. setFont, setBackground, or setForeground. * Note that if the current component is inheriting its foreground, * background, or font from its container, then no event will be * fired in response to a change in the inherited property. *

* This method will migrate to java.awt.Component in the next major JDK release * * @param listener The PropertyChangeListener to be added */ public synchronized void addPropertyChangeListener(PropertyChangeListener listener) { if (changeSupport == null) { changeSupport = new java.beans.PropertyChangeSupport(this); } changeSupport.addPropertyChangeListener(listener); } /** * Remove a PropertyChangeListener from the listener list. * This removes a PropertyChangeListener that was registered * for all properties. *

* This method will migrate to java.awt.Component in the next major JDK release * * @param listener The PropertyChangeListener to be removed */ public synchronized void removePropertyChangeListener(PropertyChangeListener listener) { if (changeSupport != null) { changeSupport.removePropertyChangeListener(listener); } } /** * Support for reporting constrained property changes. This method can be called * when a constrained property has changed and it will send the appropriate * PropertyChangeEvent to any registered VetoableChangeListeners. *

* This method will migrate to java.awt.Component in the next major JDK release * * @exception PropertyVetoException when the attempt to set the property is vetoed * by the receiver. */ protected void fireVetoableChange(String propertyName, Object oldValue, Object newValue) throws java.beans.PropertyVetoException { if (vetoableChangeSupport == null) { return; } vetoableChangeSupport.fireVetoableChange(propertyName, oldValue, newValue); } /** * Add a VetoableChangeListener to the listener list. * The listener is registered for all properties. *

* This method will migrate to java.awt.Component in the next major JDK release * * @param listener The VetoableChangeListener to be added */ public synchronized void addVetoableChangeListener(VetoableChangeListener listener) { if (vetoableChangeSupport == null) { vetoableChangeSupport = new java.beans.VetoableChangeSupport(this); } vetoableChangeSupport.addVetoableChangeListener(listener); } /** * Remove a VetoableChangeListener from the listener list. * This removes a VetoableChangeListener that was registered * for all properties. *

* This method will migrate to java.awt.Component in the next major JDK release * * @param listener The VetoableChangeListener to be removed */ public synchronized void removeVetoableChangeListener(VetoableChangeListener listener) { if (vetoableChangeSupport == null) { return; } vetoableChangeSupport.removeVetoableChangeListener(listener); } /** * Returns the top-level ancestor of this component (either the * containing Window or Applet), or null if this component has not * been added to any container. * * @return the top-level Container which this component is in. */ public Container getTopLevelAncestor() { for(Container p = this; p != null; p = p.getParent()) { if(p instanceof Window || p instanceof Applet) { return p; } } return null; } /** * Registers listener so that it will receive AncestorEvents * when it or any of its ancestors move or are made visible / invisible. * Events are also sent when the component or its ancestors are added * or removed from the Component hierarchy *

* This method will migrate to java.awt.Component in the next major JDK release * * @see AncestorEvent */ public void addAncestorListener(AncestorListener listener) { if (ancestorNotifier == null) { ancestorNotifier = new AncestorNotifier(this); } ancestorNotifier.addAncestorListener(listener); } /** * Unregisters listener so that it will no longer receive * AncestorEvents *

* This method will migrate to java.awt.Component in the next major JDK release * * @see #addAncestorListener */ public void removeAncestorListener(AncestorListener listener) { if (ancestorNotifier == null) { return; } ancestorNotifier.removeAncestorListener(listener); if (ancestorNotifier.listenerList.getListenerList().length == 0) { ancestorNotifier.removeAllListeners(); ancestorNotifier = null; } } /** * Notification to this component that it now has a parent component. * When this method is invoked, the chain of parent components is * set up with KeyboardAction event listeners. * * @see #registerKeyboardAction */ public void addNotify() { super.addNotify(); firePropertyChange("ancestor", null, getParent()); } /** * Notification to this component that it no longer has a parent component. * When this method is invoked, any KeyboardActions set up in the * the chain of parent components are removed. * * @see #registerKeyboardAction */ public void removeNotify() { super.removeNotify(); // This isn't strictly correct. The event shouldn't be // fired until *after* the parent is set to null. But // we only get notified before that happens firePropertyChange("ancestor", getParent(), null); } /** * Adds the specified region to the dirty region list if the component * is showing. The component will be repainted after all of the * currently pending events have been dispatched. * * @see java.awt.Component#isShowing * @see RepaintManager#addDirtyRegion */ public void repaint(long tm, int x, int y, int width, int height) { RepaintManager.currentManager(this).addDirtyRegion(this, x, y, width, height); } /** * Adds the specified region to the dirty region list if the component * is showing. The component will be repainted after all of the * currently pending events have been dispatched. * * @see java.awt.Component#isShowing * @see RepaintManager#addDirtyRegion */ public void repaint(Rectangle r) { repaint(0,r.x,r.y,r.width,r.height); } /** * Support for deferred automatic layout. *

* Calls invalidate() and then adds this components validateRoot * to a list of components that need to be validated. Validation * will occur after all currently pending events have been dispatched. * By default only JScrollPane.isValidateRoot() returns true. In other * words after this method is called, the first JScrollPane that * contains this component (if any) will be validated. * * @see java.awt.Component#invalidate * @see java.awt.Container#validate * @see #isValidateRoot * @see RepaintManager#addInvalidComponent */ public void revalidate() { if (SwingUtilities.isEventDispatchThread()) { invalidate(); RepaintManager.currentManager(this).addInvalidComponent(this); } else { Runnable callRevalidate = new Runnable() { public void run() { revalidate(); } }; SwingUtilities.invokeLater(callRevalidate); } } /** * If this method returns true, revalidate() calls by descendants of * this component will cause the entire tree beginning with this root * to be validated. Returns false by default. JScrollPane overrides * this method and returns true. * * @return false * @see #revalidate * @see java.awt.Component#invalidate * @see java.awt.Container#validate */ public boolean isValidateRoot() { return false; } /** * Returns true if this component tiles its children, i.e. if * it can guarantee that the children will not overlap. The * repainting system is substantially more efficient in this * common case. JComponent subclasses that can't make this * guarantee, e.g. JLayeredPane, should override this method * to return false. * * @return true if this components children don't overlap */ public boolean isOptimizedDrawingEnabled() { return true; } /** * Paint the specified region in this component and all of its * descendants that overlap the region, immediately. *

* It's rarely neccessary to call this method. In most cases it's * more efficient to call repaint which defers the actual painting * and can collapse redundant requests into a single paint call. * This method is useful if one needs to update the display while * the current event is being dispatched. * * @see #repaint */ public void paintImmediately(int x,int y,int w, int h) { Component c = this; Component parent; Rectangle bounds; if(!isShowing()) { return; } while(!((JComponent)c).isOpaque()) { parent = c.getParent(); if(parent != null) { if(c instanceof JComponent) { bounds = ((JComponent)c)._bounds; } else { bounds = c.getBounds(); } x += bounds.x; y += bounds.y; c = parent; } else { break; } if(!(c instanceof JComponent)) { break; } } if(c instanceof JComponent) { ((JComponent)c)._paintImmediately(x,y,w,h); } else { c.repaint(x,y,w,h); } } /** * Paint the specified region now. *

* This method will migrate to java.awt.Component in the next major JDK release */ public void paintImmediately(Rectangle r) { paintImmediately(r.x,r.y,r.width,r.height); } void _paintImmediately(int x, int y, int w, int h) { Graphics g; Container c; Rectangle clip = new Rectangle(x,y,w,h); Rectangle b; if(tmpRect == null) { tmpRect = new Rectangle(); } tmpRect.x=tmpRect.y=0; int offsetX=0,offsetY=0; int bufferOffsetX=0,bufferOffsetY=0; boolean hasBuffer = false; JComponent bufferedComponent = null; JComponent paintingComponent = this; RepaintManager repaintManager = RepaintManager.currentManager(this); for (c = this; c != null && !(c instanceof Window) && !(c instanceof Applet); c = c.getParent()) { if ((c instanceof JComponent) && !(((JComponent)c).isOptimizedDrawingEnabled())) { paintingComponent = (JComponent)c; offsetX = offsetY = 0; hasBuffer = false; /** Get rid of any buffer since we draw from here and * we might draw something larger */ } if(repaintManager.isDoubleBufferingEnabled() && (c instanceof JComponent) && ((JComponent)c).isDoubleBuffered()) { hasBuffer = true; bufferedComponent = (JComponent)c; bufferOffsetX = offsetX; bufferOffsetY = offsetY; } if(c instanceof JComponent) { b = ((JComponent)c)._bounds; } else { b = c.getBounds(); } tmpRect.width = b.width; tmpRect.height = b.height; SwingUtilities.computeIntersection(tmpRect.x,tmpRect.y,tmpRect.width,tmpRect.height,clip); clip.x += b.x; clip.y += b.y; offsetX += b.x; offsetY += b.y; } if(c == null || c.getPeer() == null) { return; } clip.x -= offsetX; clip.y -= offsetY; try { g = SwingGraphics.createSwingGraphics(paintingComponent.getGraphics()); } catch(NullPointerException e) { g = null; e.printStackTrace(); } if(g == null) { System.err.println("In paintImmediately null graphics"); return; } if(hasBuffer) { Image offscreen = repaintManager.getOffscreenBuffer(bufferedComponent,clip.width,clip.height); paintWithBuffer(paintingComponent,g,clip,offscreen); g.dispose(); } else { g.setClip(clip.x,clip.y,clip.width,clip.height); try { paintingComponent.paint(g); } finally { g.dispose(); } } } private void paintWithBuffer(JComponent paintingComponent,Graphics g,Rectangle clip,Image offscreen) { Graphics og = SwingGraphics.createSwingGraphics(offscreen.getGraphics()); int bw = offscreen.getWidth(null); int bh = offscreen.getHeight(null); int x,y,maxx,maxy; if(bw > clip.width) { bw = clip.width; } if(bh > clip.height) { bh = clip.height; } try { paintingComponent.setFlag(ANCESTOR_USING_BUFFER,true); paintingComponent.setFlag(IS_PAINTING_TILE,true); for(x = clip.x, maxx = clip.x+clip.width; x < maxx ; x += bw ) { for(y=clip.y, maxy = clip.y + clip.height; y < maxy ; y += bh) { if((y+bh) >= maxy && (x+bw) >= maxx) { paintingComponent.setFlag(IS_PAINTING_TILE,false); } og.translate(-x,-y); og.setClip(x,y,bw,bh); paintingComponent.paint(og); g.setClip(x,y,bw,bh); g.drawImage(offscreen,x,y,paintingComponent); og.translate(x,y); } } } finally { paintingComponent.setFlag(ANCESTOR_USING_BUFFER,false); paintingComponent.setFlag(IS_PAINTING_TILE,false); og.dispose(); } } private void setFlag(int aFlag, boolean aValue) { if(aValue) { flags |= (1 << aFlag); } else { flags &= ~(1 << aFlag); } } private boolean getFlag(int aFlag) { int mask = (1 << aFlag); return ((flags & mask) == mask); } /** Buffering **/ /** Set whether the receiving component should use a buffer to paint. * If set to true, all the drawing from this component will be done * in an offscreen painting buffer. The offscreen painting buffer will * the be copied onto the screen. * Swing's painting system always use a maximum of one double buffer. * If a Component is buffered and one of its ancestor is also buffered, * the ancestor buffer will be used. */ public void setDoubleBuffered(boolean aFlag) { setFlag(IS_DOUBLE_BUFFERED,aFlag); } /** Return whether the receiving component should use a buffer to paint. **/ public boolean isDoubleBuffered() { return getFlag(IS_DOUBLE_BUFFERED); } /** * Returns the JRootPane ancestor for a component * * @return the JRootPane that contains this component, * or null if no JRootPane is found */ public JRootPane getRootPane() { return SwingUtilities.getRootPane(this); } /** Serialization **/ /* private void writeObject(ObjectOutputStream s) throws IOException { s.defaultWriteObject(); } private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { s.defaultReadObject(); updateUI(); } */ }